export default class WS {
  constructor(url, options) {
    let opt,
      wsUrl,
      option,
      client,
      trigger,
      parseJson,
      heartBeatTimer,
      reconnectTimer,
      createSocket,
      sendHeartBeat,
      close,
      connect,
      reconnect,
      destroy;
    opt = {
      heartBeatMsg: 'heart beat',
      heartBeatTimeout: 30 * 1000,
      autoReconnect: 30 * 1000,
      autoConnect: true,
      autoReconnectOnClose: true,
      onError: () => {},
      onClose: () => {},
      onOpen: () => {},
      onMessage: () => {},
    };
    wsUrl = url;
    option = Object.assign({}, opt, options);
    trigger = (func, params) => {
      return typeof func === 'function' ? func(params) : undefined;
    };
    parseJson = (json) => {
      if (typeof json === 'string' && /^\[.*]$|^{.*}$/.test(json)) {
        let defaultValue = /^\[.*]$/.test(json) ? [] : {};
        let res;
        try {
          res = JSON.parse(json);
          return res ? res : defaultValue;
        } catch (error) {
          return defaultValue;
        }
      }
      return json;
    };
    createSocket = (url) => {
      if ('WebSocket' in window) {
        return new WebSocket(url);
      } else if ('MozWebSocket' in window) {
        return new MozWebSocket(url);
      } else {
        throw new Error('当前浏览器不支持websocket协议,建议使用最新360浏览器');
      }
    };
    sendHeartBeat = () => {
      if (client && client.readyState === 1) {
        const { heartBeatMsg, heartBeatTimeout } = option;
        client.send(JSON.stringify(heartBeatMsg));
        clearTimeout(heartBeatTimer);
        heartBeatTimer = setTimeout(
          () => {
            sendHeartBeat();
          },
          typeof heartBeatTimeout === 'number' && heartBeatTimeout > 0 ? heartBeatTimeout : opt.heartBeatTimeout
        );
      }
    };
    connect = () => {
      client = createSocket(wsUrl);
      if (client) {
        client.onerror = () => {
          // console.log('websocket client on error');
          reconnect();
          trigger(option.onError);
        };
        client.onclose = () => {
          // console.log('websocket client closed');
          if (option.autoReconnectOnClose === true) {
            reconnect();
          }
          trigger(option.onClose);
        };
        client.onopen = () => {
          // console.log('websocket client opened');
          clearTimeout(reconnectTimer);
          sendHeartBeat(client, option.heartBeatMsg);
          trigger(option.onOpen);
        };
        client.onmessage = (res) => {
          // console.log('websocket client on message');
          trigger(option.onMessage, parseJson(res.data));
        };
      }
    };
    reconnect = () => {
      const { autoReconnect } = option;
      const time = typeof autoReconnect === 'number' && autoReconnect > 1000 ? autoReconnect : opt.autoReconnect;
      // console.log('websocket is going to reconnect');
      clearTimeout(reconnectTimer);
      reconnectTimer = setTimeout(() => {
        close();
        connect();
        // console.log('websocket is reconnecting');
      }, time);
    };
    close = () => {
      if (client && typeof client.close === 'function') {
        client.close();
      }
    };
    destroy = () => {
      clearTimeout(heartBeatTimer);
      clearTimeout(reconnectTimer);
      close();
      this.connect = null;
      this.reconnect = null;
      this.close = null;
      this.destroy = null;
      opt = null;
      wsUrl = null;
      option = null;
      client = null;
      trigger = null;
      parseJson = null;
      heartBeatTimer = null;
      reconnectTimer = null;
      createSocket = null;
      sendHeartBeat = null;
      close = null;
      connect = null;
      reconnect = null;
      destroy = null;
    };

    if (option.autoConnect === true) {
      connect();
    }

    this.connect = connect;
    this.reconnect = reconnect;
    this.close = close;
    this.destroy = destroy;
  }
}
